home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pine / mailcap.c < prev    next >
C/C++ Source or Header  |  1996-03-14  |  31KB  |  1,149 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: mailcap.c,v 1.38 1996/03/15 07:13:42 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.            T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade, Mike Seibel and David Miller
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    4545 Builiding, JE-20
  13.    Seattle, Washington, 98195, USA
  14.    Internet: mikes@cac.washington.edu
  15.              dlm@cac.wasington.edu
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *                   Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *                   Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.    ******************************************************* 
  41.    Mailcap support is based in part on Metamail version 2.7:
  42.       Author:  Nathaniel S. Borenstein, Bellcore
  43.  
  44.    Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
  45.  
  46.    Permission to use, copy, modify, and distribute this material
  47.    for any purpose and without fee is hereby granted, provided
  48.    that the above copyright notice and this permission notice
  49.    appear in all copies, and that the name of Bellcore not be
  50.    used in advertising or publicity pertaining to this
  51.    material without the specific, prior written permission
  52.    of an authorized representative of Bellcore.  BELLCORE
  53.    MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
  54.    OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",
  55.    WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
  56.    ******************************************************* 
  57.  
  58.    ******************************************************* 
  59.    MIME-TYPE support based on code contributed
  60.    by Hans Drexler <drexler@mpi.nl>.  His comment:
  61.  
  62.    Mime types makes mime assign attachment types according
  63.    to file name extensions found in a system wide file
  64.    ``/usr/local/lib/mime.types'' and a user specific file
  65.    ``~/.mime.types'' . These files specify file extensions
  66.    that will be connected to a mime type.
  67.    ******************************************************* 
  68.  
  69.  
  70.   ----------------------------------------------------------------------*/
  71.  
  72. /*======================================================================
  73.        mailcap.c
  74.  
  75.        Functions to support "mailcap" (RFC1524) to help us read MIME
  76.        segments of various types, and ".mime.types" ala NCSA Mosaic to
  77.        help us attach various types to outbound MIME segments.
  78.  
  79.  ====*/
  80.  
  81. #include "headers.h"
  82.  
  83. /*
  84.  * We've decided not to implement the RFC1524 standard minimum path, because
  85.  * some of us think it is harder to debug a problem when you may be misled
  86.  * into looking at the wrong mailcap entry.  Likewise for MIME.Types files.
  87.  */
  88. #if defined(DOS) || defined(OS2)
  89. #define MC_PATH_SEPARATOR ';'
  90. #define MT_PATH_SEPARATOR ';'
  91. #define    MT_USER_FILE      "mimetype"
  92. #else /* !DOS */
  93. #define MC_PATH_SEPARATOR ':'
  94. #define MC_STDPATH         \
  95.         ".mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap"
  96. #define MT_PATH_SEPARATOR ':'
  97. #define MT_STDPATH         \
  98.         ".mime.types:/etc/mime.types:/usr/local/lib/mime.types"
  99. #endif /* !DOS */
  100.  
  101. #define LINE_BUF_SIZE      2000
  102.  
  103. typedef struct mcap_entry {
  104.     struct mcap_entry *next;
  105.     int                needsterminal;   /* currently unused */
  106.     char              *contenttype;
  107.     char              *command;
  108.     char              *testcommand;
  109.     char              *label;           /* unused */
  110.     char              *printcommand;    /* unused */
  111. } MailcapEntry;
  112.  
  113. MailcapEntry *mc_head = NULL,
  114.          *mc_tail = NULL;
  115. static int    mc_is_initialized = 0;
  116.  
  117.  
  118. /*
  119.  * Internal prototypes
  120.  */
  121. void      mc_addtolist PROTO((MailcapEntry *));
  122. char     *mc_bld_test_cmd PROTO((char *, int, char *, PARAMETER *));
  123. char     *mc_cmd_bldr PROTO((char *, int, char *, PARAMETER *, char *));
  124. int       mc_ctype_match PROTO((int, char *, char *));
  125. MailcapEntry *mc_get_command PROTO((int, char *, PARAMETER *));
  126. int       mc_get_entry PROTO((FILE *, MailcapEntry *));
  127. char     *mc_get_next_piece PROTO((char *, char **));
  128. void      mc_init PROTO((void));
  129. int       mc_passes_test PROTO((MailcapEntry *, int, char *, PARAMETER *));
  130. void      mc_process_file PROTO((char *));
  131. void      mt_get_file_ext PROTO((char *, char **));
  132. int      mt_use_mime_type PROTO((BODY *, char *));
  133. int      mt_browse_types_file PROTO((BODY *, char *, char *));
  134. int      mt_translate_type PROTO((char *));
  135. int      mt_traverse_types_file PROTO((BODY *, char *, FILE *));
  136.  
  137.  
  138.  
  139. /*
  140.  * mc_init - Run down the path gathering all the mailcap entries.
  141.  *           Returns with the Mailcap list built.
  142.  */
  143. void
  144. mc_init()
  145. {
  146.     char *s,
  147.      *pathcopy,
  148.      *path;
  149.   
  150.     if(mc_is_initialized)
  151.       return;
  152.  
  153.     dprint(2, (debugfile, "- mc_init -\n"));
  154.  
  155.     if(ps_global->VAR_MAILCAP_PATH)
  156.       /* there may need to be an override specific to pine */
  157.       pathcopy = cpystr(ps_global->VAR_MAILCAP_PATH);
  158.     else if((path = getenv("MAILCAPS")) != (char *)NULL)
  159.       /* rfc1524 specifies MAILCAPS as a path override */
  160.       pathcopy = cpystr(path);
  161.     else{
  162. #if defined(DOS) || defined(OS2)
  163.         /*
  164.      * This gets interesting.  Since we don't have any standard location
  165.      * for config/data files, look in the same directory as the PINERC
  166.      * and the same dir as PINE.EXE.  This is similar to the UNIX
  167.      * situation with personal config info coming before 
  168.      * potentially shared config data...
  169.      */
  170.     s = last_cmpnt(ps_global->pinerc);
  171.     if(s != NULL){
  172.         strncpy(tmp_20k_buf, ps_global->pinerc, s - ps_global->pinerc);
  173.         tmp_20k_buf[s - ps_global->pinerc] = '\0';
  174.     }
  175.     else
  176.       strcpy(tmp_20k_buf, ".\\");
  177.  
  178.     sprintf(tmp_20k_buf + strlen(tmp_20k_buf),
  179.             "MAILCAP%c%s\\MAILCAP",
  180.             MC_PATH_SEPARATOR,
  181.         ps_global->pine_dir);
  182. #else /* !DOS */
  183.     build_path(tmp_20k_buf, ps_global->home_dir, MC_STDPATH);
  184. #endif /* !DOS */
  185.     pathcopy = cpystr(tmp_20k_buf);
  186.     }
  187.  
  188.     path = pathcopy;            /* overloaded "path" */
  189.  
  190.     /*
  191.      * Insert an entry for the image-viewer variable from .pinerc, if present.
  192.      */
  193.     if(ps_global->VAR_IMAGE_VIEWER && *ps_global->VAR_IMAGE_VIEWER){
  194.     MailcapEntry mc;
  195.     char *p;
  196.  
  197.     p = tmp_20k_buf;
  198.     sstrcpy(&p, ps_global->VAR_IMAGE_VIEWER);
  199.     sstrcpy(&p, " %s");
  200.     mc.command       = cpystr(tmp_20k_buf);
  201.     mc.contenttype   = cpystr("image/*");
  202.     mc.testcommand   = (char *)NULL;
  203.     mc.printcommand  = (char *)NULL;
  204.     mc.needsterminal = 0;
  205.     mc.label         = cpystr("Pine Image Viewer");
  206.     mc.next          = (MailcapEntry *)NULL;
  207.  
  208.     mc_addtolist(&mc);
  209.     dprint(5, (debugfile, "mailcap: using image-viewer=%s\n", 
  210.            ps_global->VAR_IMAGE_VIEWER));
  211.     }
  212.  
  213.     dprint(7, (debugfile, "mailcap: path: %s\n", path));
  214.     while(path){
  215.     s = strindex(path, MC_PATH_SEPARATOR);
  216.     if(s)
  217.       *s++ = '\0';
  218.     mc_process_file(path);
  219.     path = s;
  220.     }
  221.   
  222.     if(pathcopy)
  223.       fs_give((void **)&pathcopy);
  224.  
  225.     mc_is_initialized++;
  226.  
  227. #ifdef DEBUG
  228.     if(debug >= 11){
  229.     MailcapEntry *mc;
  230.     int i = 0;
  231.  
  232.     dprint(11, (debugfile, "Collected mailcap entries\n"));
  233.     for(mc = mc_head; mc; mc = mc->next){
  234.  
  235.         dprint(11, (debugfile, "%d: ", i++));
  236.         if(mc->label)
  237.           dprint(11, (debugfile, "%s\n", mc->label));
  238.         if(mc->contenttype)
  239.           dprint(11, (debugfile, "   %s", mc->contenttype));
  240.         if(mc->command)
  241.           dprint(11, (debugfile, "   command: %s\n", mc->command));
  242.         if(mc->testcommand)
  243.           dprint(11, (debugfile, "   testcommand: %s", mc->testcommand));
  244.         if(mc->printcommand)
  245.           dprint(11, (debugfile, "   printcommand: %s", mc->printcommand));
  246.         dprint(11, (debugfile, "   needsterminal %d\n", mc->needsterminal));
  247.     }
  248.     }
  249. #endif /* DEBUG */
  250. }
  251.  
  252.  
  253. /*
  254.  * Add all the entries from this file onto the Mailcap list.
  255.  */
  256. void
  257. mc_process_file(file)
  258. char *file;
  259. {
  260.     MailcapEntry mc;
  261.     int file_check;
  262.     FILE *fp;
  263.     char filebuf[MAXPATH+1];
  264.   
  265.     dprint(2, (debugfile, "mailcap: process_file: %s\n", file));
  266.  
  267.     (void)strncpy(filebuf, file, MAXPATH);
  268.     filebuf[MAXPATH] = '\0';
  269.     file = fnexpand(filebuf, MAXPATH);
  270.     dprint(7, (debugfile, "mailcap: processing file: %s\n", file));
  271.     file_check = is_writable_dir(file);
  272.     switch(file_check){
  273.       case 0: case 1: /* is a directory */
  274.     dprint(2, (debugfile, "mailcap: %s is a directory, should be a file\n",
  275.         file));
  276.     return;
  277.  
  278.       case 2: /* ok */
  279.     break;
  280.  
  281.       case 3: /* doesn't exist */
  282.     dprint(5, (debugfile, "mailcap: %s doesn't exist\n", file));
  283.     return;
  284.  
  285.       default:
  286.     panic("Programmer botch in mc_process_file");
  287.     /*NOTREACHED*/
  288.     }
  289.  
  290.     fp = fopen(file, "r");
  291.  
  292.     if(fp){
  293.     while(mc_get_entry(fp, &mc))
  294.       mc_addtolist(&mc);
  295.     
  296.     (void)fclose(fp);
  297.     }
  298. }
  299.  
  300.  
  301. /*
  302.  * Find the next entry in the file pointed to by fp and fill in mc.
  303.  *
  304.  * Returns 1 if it found another entry, 0 if no more entries in file.
  305.  */
  306. int
  307. mc_get_entry(fp, mc)
  308. FILE *fp;
  309. MailcapEntry *mc;
  310. {
  311.     char *s, *t, *cont, *raw_entry;
  312.     char  linebuf[LINE_BUF_SIZE];
  313.     int   raw_entry_alloc, len;
  314.  
  315.     mc->contenttype   = (char *)NULL;
  316.     mc->command       = (char *)NULL;
  317.     mc->testcommand   = (char *)NULL;
  318.     mc->label         = (char *)NULL;
  319.     mc->printcommand  = (char *)NULL;
  320.     mc->needsterminal = 0;
  321.     mc->next          = (MailcapEntry *)NULL;
  322.  
  323. #define ALLOC_INCREMENT  1000
  324.     raw_entry_alloc = ALLOC_INCREMENT;
  325.     raw_entry = (char *)fs_get((size_t)raw_entry_alloc + 1);
  326. try_again:
  327.     *raw_entry = '\0';
  328.  
  329.     /* handle continuation lines and comments */
  330.     while(fgets(linebuf, LINE_BUF_SIZE, fp)){
  331.     if(*linebuf == '#' || *linebuf == '\n') /* skip comments and blanks */
  332.       continue;
  333.         len = strlen(linebuf);
  334.         if(linebuf[len-1] == '\n')
  335.       linebuf[--len] = '\0';
  336.         if(len + strlen(raw_entry) > raw_entry_alloc){
  337.             raw_entry_alloc += ALLOC_INCREMENT;
  338.             fs_resize((void **)&raw_entry, (size_t)raw_entry_alloc+1);
  339.         }
  340.         if(linebuf[len-1] == '\\'){
  341.             linebuf[len-1] = '\0';
  342.             (void)strcat(raw_entry, linebuf);
  343.         }
  344.     else{
  345.             (void)strcat(raw_entry, linebuf);
  346.             break;
  347.         }
  348.     }
  349.  
  350.     /* skip leading space in line */
  351.     for(cont = raw_entry; *cont && isspace(*cont); ++cont)
  352.       ;/* do nothing */
  353.     if(!*cont){  /* blank entry */
  354.     if(feof(fp)){  /* end of file */
  355.         if(raw_entry)
  356.           fs_give((void **)&raw_entry);
  357.         dprint(7, (debugfile, "mailcap: end of file\n"));
  358.         return 0;
  359.     }
  360.     goto try_again;
  361.     }
  362.  
  363.     s = strindex(cont, ';');
  364.     if(!s){
  365.     dprint(2, (debugfile, "mailcap: ignoring entry, no semicolon: %s\n",
  366.         raw_entry));
  367.     goto try_again;
  368.     }
  369.  
  370.     dprint(8, (debugfile, "mailcap: processing entry: %s\n", raw_entry));
  371.  
  372.     *s++ = '\0';
  373.  
  374.     /* trim white space, convert to lowercase */
  375.     mc->contenttype = cpystr(strclean(cont));
  376.     dprint(9, (debugfile, "mailcap: content_type: %s\n", mc->contenttype));
  377.  
  378.     /* make implicit wildcard explicit */
  379.     if (strindex(mc->contenttype, '/') == NULL){
  380.     fs_resize((void **)&mc->contenttype, strlen(mc->contenttype) + 3);
  381.         (void)strcat(mc->contenttype, "/*");
  382.     }
  383.  
  384.     t = mc_get_next_piece(s, &mc->command);
  385.     removing_leading_white_space(mc->command);
  386.     dprint(9, (debugfile, "mailcap: command: %s\n", mc->command));
  387.  
  388.     if(!t){
  389.     fs_give((void **)&raw_entry);
  390.     return 1;
  391.     }
  392.  
  393.     /* process fields */
  394.     dprint(9, (debugfile, "mailcap: flags: %s\n", t));
  395.     for(s = t; s; s = t){
  396.     char *arg, *eq;
  397.     
  398.     t = mc_get_next_piece(s, &arg);
  399.     if(*arg)
  400.       removing_leading_white_space(arg);
  401.     eq = strindex(arg, '=');
  402.     if(eq)
  403.       *eq++ = '\0';
  404.  
  405.     if(*arg)
  406.       removing_trailing_white_space(arg);
  407.     if(*arg){
  408.         if(!strcmp(arg, "needsterminal")){
  409.             mc->needsterminal = 1;
  410.             dprint(9, (debugfile, "mailcap: set flag needsterminal\n"));
  411.         }
  412.         else if(!strcmp(arg, "copiousoutput")){
  413.             ;/* not used */
  414.             dprint(9, (debugfile,
  415.             "mailcap: not using flag copiousoutput\n"));
  416.         }
  417.         else if(eq && !strcmp(arg, "test")){
  418.             mc->testcommand = cpystr(eq);
  419.             dprint(9, (debugfile, "mailcap: testcommand=%s\n",
  420.             mc->testcommand));
  421.         }
  422.         else if(eq && !strcmp(arg, "description")){
  423.             mc->label = cpystr(eq);
  424.             dprint(9, (debugfile, "mailcap: label=%s\n",
  425.             mc->label));
  426.         }
  427.         else if(eq && !strcmp(arg, "print")){
  428.             mc->printcommand = cpystr(eq);
  429.             dprint(9, (debugfile, "mailcap: printcommand=%s\n",
  430.             mc->printcommand));
  431.         }
  432.         else if(eq && !strcmp(arg, "compose")){
  433.             ;/* not used */
  434.             dprint(9, (debugfile, "mailcap: not using compose=%s\n", eq));
  435.         }
  436.         else if(eq && !strcmp(arg, "composetyped")){
  437.             ;/* not used */
  438.             dprint(9, (debugfile, "mailcap: not using composetyped=%s\n",
  439.             eq));
  440.         }
  441.         else if(eq && !strcmp(arg, "textualnewlines")){
  442.             ;/* not used */
  443.             dprint(9, (debugfile,
  444.             "mailcap: not using texttualnewlines=%s\n", eq));
  445.         }
  446.         else if(eq && !strcmp(arg, "edit")){
  447.             ;/* not used */
  448.             dprint(9, (debugfile, "mailcap: not using edit=%s\n", eq));
  449.         }
  450.         else if(eq && !strcmp(arg, "x11-bitmap")){
  451.             ;/* not used */
  452.             dprint(9, (debugfile, "mailcap: not using x11-bitmap=%s\n",
  453.             eq));
  454.         }
  455.         else
  456.           dprint(9, (debugfile, "mailcap: ignoring unknown flag: %s\n",
  457.           arg));
  458.     }
  459.  
  460.     if(arg)
  461.       fs_give((void **)&arg);
  462.     }
  463.  
  464.     fs_give((void **)&raw_entry);
  465.     return 1;
  466. }
  467.  
  468.  
  469. /*
  470.  * s is a pointer to the current position in the mailcap entry
  471.  *
  472.  * "result" is a pointer to point at an alloc'd copy of the current piece, up
  473.  * to the next ";", and the return value is a pointer to the piece after
  474.  * the ";" (the next "s").
  475.  */
  476. char *
  477. mc_get_next_piece(s, result)
  478. char *s, **result;
  479. {
  480.     char *s2;
  481.     int quoted = 0;
  482.  
  483.     s2 = (char *)fs_get(strlen(s) * 2 + 1); /* absolute max, if all % signs */
  484.  
  485.     *result = s2;
  486.  
  487.     while(s && *s){
  488.     if(quoted){
  489.         if(*s == '%') /* quoted % turns into %% */
  490.           *s2++ = '%';
  491.       
  492.         *s2++ = *s++;
  493.         quoted = 0;
  494.     }
  495.     else{
  496.         if(*s == ';'){  
  497.         *s2 = '\0';
  498.         dprint(9, (debugfile, "mailcap: mc_get_next_piece: %s\n",
  499.             *result));
  500.         return(++s);
  501.         }
  502. #if defined(DOS) || defined(OS2)
  503.         /*
  504.          * RFC 1524 says that backslash is used to quote
  505.          * the next character, but since backslash is part of pathnames
  506.          * on DOS we're afraid people will not put double backslashes
  507.          * in their mailcap files.  Therefore, we violate the RFC by
  508.          * looking ahead to the next character.  If it looks like it
  509.          * is just part of a pathname, then we consider a single
  510.          * backslash to *not* be a quoting character, but a literal
  511.          * backslash instead.
  512.          */
  513.         else if(*s == '\\'){
  514.         int t;
  515.  
  516.         t = *(s+1);
  517.         /*
  518.          * If next char is any of these, treat the backslash
  519.          * that preceded it like a regular character.
  520.          */
  521.         if(t && isascii(t) &&
  522.              (isalnum(t) || t == '_' || t == '+' || t == '-' ||
  523.               t == '=' || t == '~')){
  524.             *s2++ = *s++;
  525.         }
  526.         /* otherwise, treat it as a quoting backslash */
  527.         else{
  528.             quoted++;
  529.             ++s;
  530.         }
  531.         }
  532. #else  /* !DOS */
  533.         /* backslash quotes the next character */
  534.         else if(*s == '\\'){
  535.         quoted++;
  536.         ++s;
  537.         }
  538. #endif  /* !DOS */
  539.         else
  540.           *s2++ = *s++;
  541.     } 
  542.     }
  543.  
  544.     *s2 = '\0';
  545.  
  546.     dprint(9, (debugfile, "mailcap: mc_get_next_piece(last): %s\n", *result));
  547.  
  548.     return NULL;
  549. }
  550.      
  551.  
  552. /*
  553.  * Returns the mailcap entry for type/subtype from the successfull
  554.  * mailcap entry, or NULL if none.  Command string still contains % stuff.
  555.  */
  556. MailcapEntry *
  557. mc_get_command(type, subtype, params)
  558. int        type;
  559. char      *subtype;
  560. PARAMETER *params;
  561. {
  562.     MailcapEntry *mc;
  563.  
  564.     dprint(4, (debugfile, "- mc_get_command(%s/%s) -\n",
  565.     body_type_names(type), subtype));
  566.  
  567.     for(mc = mc_head; mc; mc = mc->next){
  568.     if(mc_ctype_match(type, subtype, mc->contenttype) &&
  569.        mc_passes_test(mc, type, subtype, params)){
  570.  
  571.         dprint(9, (debugfile, 
  572.              "mc_get_command: type=%s/%s, command=%s\n", 
  573.              body_type_names(type), subtype, mc->command));
  574.         return(mc);
  575.     }
  576.     }
  577.     return((MailcapEntry *)NULL);
  578. }
  579.  
  580.  
  581. /*
  582.  * Check whether the pattern "pat" matches this type/subtype.
  583.  * Returns 1 if it does, 0 if not.
  584.  */
  585. int
  586. mc_ctype_match(type, subtype, pat)
  587. int   type;
  588. char *subtype;
  589. char *pat;
  590. {
  591.     int  len, rv;
  592.     char *type_txt;
  593.     char *full_ctype;
  594.     char *p;
  595.  
  596.     type_txt = body_type_names(type);
  597.     len = strlen(subtype) + strlen(type_txt);
  598.     full_ctype = (char *)fs_get((size_t)len + 1 + 1);
  599.     p = full_ctype;
  600.     sstrcpy(&p, type_txt);
  601.     sstrcpy(&p, "/");
  602.     sstrcpy(&p, subtype);
  603.  
  604.     dprint(4, (debugfile, 
  605.        "mc_ctype_match: trying entry %s for %s\n", pat, full_ctype));
  606.         
  607.     rv = 0;
  608.  
  609.     /* check for exact match */
  610.     if(!strucmp(full_ctype, pat))
  611.       rv = 1;
  612.  
  613.     /* check for wildcard match */
  614.     else if ((len=strlen(pat)) >= 2 
  615.      && (pat[--len] == '*')
  616.          && (pat[--len] == '/')
  617.          && (!struncmp(full_ctype, pat, len))
  618.          && ((full_ctype[len] == '/') || (full_ctype[len] == '\0')))
  619.       rv = 1;
  620.  
  621.     fs_give((void **)&full_ctype);
  622.     return(rv);
  623. }
  624.  
  625.  
  626. /*
  627.  * Run the test command for entry mc to see if this entry currently applies to
  628.  * applies to this type/subtype.
  629.  *
  630.  * Returns 1 if it does pass test (exits with status 0), 0 otherwise.
  631.  */
  632. int
  633. mc_passes_test(mc, type, subtype, params)
  634. MailcapEntry *mc;
  635. int           type;
  636. char         *subtype;
  637. PARAMETER    *params;
  638. {
  639.     char *cmd = NULL;
  640.     int   rv;
  641.  
  642.     dprint(2, (debugfile, "- mc_passes_test -\n"));
  643.  
  644.     if(mc->testcommand && *mc->testcommand)
  645.     cmd = mc_bld_test_cmd(mc->testcommand, type, subtype, params);
  646.     
  647.     if(!mc->testcommand || !cmd || !*cmd){
  648.     if(cmd)
  649.       fs_give((void **)&cmd);
  650.     dprint(7, (debugfile, "no test command, so Pass\n"));
  651.     return 1;
  652.     }
  653.  
  654.     rv = exec_mailcap_test_cmd(cmd);
  655.     dprint(7, (debugfile, "mc_passes_test: \"%s\" %s (rv=%d)\n",
  656.        cmd, rv ? "Failed" : "Passed", rv)) ;
  657.  
  658.     fs_give((void **)&cmd);
  659.  
  660.     return(!rv);
  661. }
  662.  
  663.  
  664. int
  665. mailcap_can_display(type, subtype, params)
  666. int       type;
  667. char     *subtype;
  668. PARAMETER *params;
  669. {
  670.     dprint(5, (debugfile, "- mailcap_can_display -\n"));
  671.  
  672.     if(!mc_is_initialized)
  673.       mc_init();
  674.  
  675.     return(mc_get_command(type, subtype, params) ? 1 : 0);
  676. }
  677.  
  678.  
  679. char *
  680. mailcap_build_command(body, tmp_file, needsterm)
  681. BODY *body;
  682. char *tmp_file;
  683. int  *needsterm;
  684. {
  685.     MailcapEntry *mc;
  686.     char         *command;
  687.  
  688.     dprint(2, (debugfile, "- mailcap_build_command -\n"));
  689.  
  690.     if(!mc_is_initialized)
  691.       mc_init();
  692.  
  693.     mc = mc_get_command((int)body->type, body->subtype, body->parameter);
  694.     if(!mc){
  695.     q_status_message(SM_ORDER, 3, 4, "Error constructing viewer command");
  696.     dprint(2, (debugfile,
  697.            "mailcap_build_command: no command string for %s/%s\n",
  698.            body_type_names((int)body->type), body->subtype));
  699.     return((char *)NULL);
  700.     }
  701.  
  702.     *needsterm = mc->needsterminal;
  703.  
  704.     command = mc_cmd_bldr(mc->command, (int)body->type,
  705.               body->subtype, body->parameter, tmp_file);
  706.  
  707.     dprint(2, (debugfile, "built command: %s\n", command));
  708.  
  709.     return(command);
  710. }
  711.  
  712.  
  713. /*
  714.  * mc_bld_test_cmd - build the command to test if the given type flies
  715.  *
  716.  *    mc_cmd_bldr's tmp_file argument is NULL as we're not going to
  717.  *    decode and write each and every MIME segment's data to a temp file
  718.  *    when no test's going to use the data anyway.
  719.  */
  720. char *
  721. mc_bld_test_cmd(controlstring, type, subtype, params)
  722.     char      *controlstring;
  723.     int        type;
  724.     char      *subtype;
  725.     PARAMETER *params;
  726. {
  727.     return(mc_cmd_bldr(controlstring, type, subtype, params, NULL));
  728. }
  729.  
  730.  
  731. /*
  732.  * mc_cmd_bldr - construct a command string to execute
  733.  *
  734.  *    If tmp_file is null, then the contents of the given MIME segment
  735.  *    is not provided.  This is useful for building the "test=" string
  736.  *    as it doesn't operate on the segment's data.
  737.  *
  738.  *    The return value is an alloc'd copy of the command to be executed.
  739.  */
  740. char *
  741. mc_cmd_bldr(controlstring, type, subtype, parameter, tmp_file)
  742.     char *controlstring;
  743.     int   type;
  744.     char *subtype;
  745.     PARAMETER *parameter;
  746.     char *tmp_file;
  747. {
  748.     char        *from, *to, *s; 
  749.     int             prefixed = 0, used_tmp_file = 0;
  750.     PARAMETER   *parm;
  751.  
  752.     dprint(8, (debugfile, "- mc_cmd_bldr -\n"));
  753.  
  754.     for(from = controlstring, to = tmp_20k_buf; *from; ++from){
  755.     if(prefixed){            /* previous char was % */
  756.         prefixed = 0;
  757.         switch(*from){
  758.           case '%':            /* turned \% into this earlier */
  759.         *to++ = '%';
  760.         break;
  761.  
  762.           case 's':            /* insert tmp_file name in cmd */
  763.         if(tmp_file){
  764.             used_tmp_file = 1;
  765.             sstrcpy(&to, tmp_file);
  766.         }
  767.         else
  768.           dprint(2, (debugfile,
  769.                  "mc_cmd_bldr: %%s in cmd but not supplied!\n"));
  770.  
  771.         break;
  772.  
  773.           case 't':            /* insert MIME type/subtype */
  774.         /* quote to prevent funny business */
  775.         *to++ = '\'';
  776.         sstrcpy(&to, body_type_names(type));
  777.         *to++ = '/';
  778.         sstrcpy(&to, subtype);
  779.         *to++ = '\'';
  780.         break;
  781.  
  782.           case '{':            /* insert requested MIME param */
  783.         s = strindex(from, '}');
  784.         if(!s){
  785.             q_status_message1(SM_ORDER, 0, 4,
  786.     "Ignoring ill-formed parameter reference in mailcap file: %s", from);
  787.             break;
  788.         }
  789.  
  790.         *s = '\0';
  791.         ++from;    /* from is the part inside the brackets now */
  792.  
  793.         for(parm = parameter; parm; parm = parm->next)
  794.           if(!strucmp(from, parm->attribute))
  795.             break;
  796.  
  797.         dprint(9, (debugfile,
  798.                "mc_cmd_bldr: parameter %s = %s\n", 
  799.                from, parm ? parm->value : "(not found)"));
  800.  
  801.         /*
  802.          * Quote parameter values for /bin/sh.
  803.          * Put single quotes around the whole thing but every time
  804.          * there is an actual single quote put it outside of the
  805.          * single quotes with a backslash in front of it.  So the
  806.          * parameter value  fred's car
  807.          * turns into       'fred'\''s car'
  808.          */
  809.         *to++ = '\'';  /* opening quote */
  810.         if(parm){
  811.             char *p;
  812.  
  813.             /*
  814.              * Copy value, but quote single quotes for /bin/sh
  815.              * Backslash quote is ignored inside single quotes so
  816.              * have to put those outside of the single quotes.
  817.              */
  818.             for(p = parm->value; *p; p++){
  819.             if(*p == '\''){
  820.                 *to++ = '\'';  /* closing quote */
  821.                 *to++ = '\\';
  822.                 *to++ = '\'';  /* below will be opening quote */
  823.             }
  824.             *to++ = *p;
  825.             }
  826.         }
  827.  
  828.         *to++ = '\'';  /* closing quote for /bin/sh */
  829.  
  830.         *s = '}'; /* restore */
  831.         from = s;
  832.         break;
  833.  
  834.         /*
  835.          * %n and %F are used by metamail to support otherwise
  836.          * unrecognized multipart Content-Types.  Pine does
  837.          * not use these since we're only dealing with the individual
  838.          * parts at this point.
  839.          */
  840.           case 'n':
  841.           case 'F':  
  842.           default:  
  843.         dprint(9, (debugfile, 
  844.            "Ignoring %s format code in mailcap file: %%%c\n",
  845.            (*from == 'n' || *from == 'F') ? "unimplemented"
  846.                           : "unrecognized",
  847.            *from));
  848.         break;
  849.         }
  850.     }
  851.     else if(*from == '%')        /* next char is special */
  852.       prefixed = 1;
  853.     else                /* regular character, just copy */
  854.       *to++ = *from;
  855.     }
  856.  
  857.     *to = '\0';
  858.  
  859.     /*
  860.      * file not specified, redirect to stdin
  861.      */
  862.     if(!used_tmp_file && tmp_file)
  863.       sprintf(to, " < %s", tmp_file);
  864.  
  865.     return(cpystr(tmp_20k_buf));
  866.  
  867.  
  868. /*
  869.  * Adds element mc to the mailcap list.
  870.  */
  871. void
  872. mc_addtolist(mc)
  873. MailcapEntry *mc;
  874. {
  875.     register MailcapEntry *new_mc;
  876.  
  877.     new_mc = (MailcapEntry *)fs_get(sizeof(MailcapEntry));
  878.  
  879.     /*
  880.      * Copy the structure, but don't copy pointed-to strings, just
  881.      * copy the pointers themselves.
  882.      */
  883.     *new_mc = *mc;
  884.  
  885.     /* add to end of list so search order is correct */
  886.     if(mc_tail)
  887.     mc_tail->next = new_mc;
  888.     else
  889.     mc_head       = new_mc;
  890.  
  891.     mc_tail = new_mc;
  892. }
  893.  
  894.  
  895. void
  896. mailcap_free()
  897. {
  898.     MailcapEntry *mc, *mc_next;
  899.  
  900.     dprint(2, (debugfile, "- mailcap_free -\n"));
  901.  
  902.     if(!mc_is_initialized)
  903.       return;
  904.     
  905.     for(mc = mc_head; mc; mc = mc_next){
  906.     mc_next = mc->next;
  907.     if(mc->contenttype)
  908.     fs_give((void **)&mc->contenttype);
  909.     if(mc->command)
  910.         fs_give((void **)&mc->command);
  911.     if(mc->testcommand)
  912.         fs_give((void **)&mc->testcommand);
  913.     if(mc->label)
  914.         fs_give((void **)&mc->label);
  915.     if(mc->printcommand)
  916.         fs_give((void **)&mc->printcommand);
  917.     fs_give((void **)&mc);
  918.     }
  919.  
  920.     mc_is_initialized = 0;
  921.     mc_head = (MailcapEntry *)NULL;
  922.     mc_tail = (MailcapEntry *)NULL;
  923. }
  924.  
  925.  
  926.  
  927. /*
  928.  * END OF MAILCAP ROUTINES, START OF MIME.TYPES ROUTINES
  929.  */
  930.  
  931.  
  932. /*
  933.  * Exported function that does the work of sniffing the mime.types
  934.  * files and filling in the body pointer if found.  Returns 1 (TRUE) if
  935.  * extension found, and body pointer filled in, 0 (FALSE) otherwise.
  936.  */
  937. int
  938. set_mime_type_by_extension(body, filename)
  939.     BODY *body;
  940.     char *filename;
  941. {
  942.     char *extension;
  943.  
  944.     mt_get_file_ext(filename, &extension);
  945.     dprint(5, (debugfile, "mt_set_new : filename=%s, extension=%s\n",
  946.            filename, extension));
  947.     return(*extension ? mt_use_mime_type(body, extension) : 0);
  948. }
  949.  
  950.  
  951. /* 
  952.  * Separate and return a pointer to the first character in the 'filename'
  953.  * character buffer that comes after the rightmost '.' character in the
  954.  * filename. (What I mean is a pointer to the filename - extension).
  955.  */
  956. void
  957. mt_get_file_ext(filename, extension)
  958.     char *filename;
  959.     char **extension;
  960. {
  961.     char *index = NULL;
  962.  
  963.     do
  964.       if(*filename == '.')
  965.     index = filename;
  966.     while (*filename++ != '\0');
  967.  
  968.     *extension = (index) ? index + 1 : filename - 1;
  969. }
  970.  
  971.  
  972. /*
  973.  * Read the mime types from appropriate files and compare the extensions.
  974.  * Set the message type if a match can be found.
  975.  */
  976. int
  977. mt_use_mime_type(body, extension)
  978.     BODY *body;
  979.     char *extension;
  980. {
  981.     char *s, *pathcopy, *path;
  982.     int   rv = 0;
  983.   
  984.     dprint(2, (debugfile, "- mt_use_mime_type -\n"));
  985.  
  986.     /* We specify MIMETYPES as a path override */
  987.     if(ps_global->VAR_MIMETYPE_PATH)
  988.       /* there may need to be an override specific to pine */
  989.       pathcopy = cpystr(ps_global->VAR_MIMETYPE_PATH);
  990.     else if((path = getenv("MIMETYPES")) != (char *)NULL)
  991.       pathcopy = cpystr(path);
  992.     else{
  993. #if defined(DOS) || defined(OS2)
  994.         /*
  995.      * This gets interesting.  Since we don't have any standard location
  996.      * for config/data files, look in the same directory as the PINERC
  997.      * and the same dir as PINE.EXE.  This is similar to the UNIX
  998.      * situation with personal config info coming before 
  999.      * potentially shared config data...
  1000.      */
  1001.     s = last_cmpnt(ps_global->pinerc);
  1002.     if(s != NULL){
  1003.         strncpy(tmp_20k_buf, ps_global->pinerc, s - ps_global->pinerc);
  1004.         tmp_20k_buf[s - ps_global->pinerc] = '\0';
  1005.     }
  1006.     else
  1007.       strcpy(tmp_20k_buf, ".\\");
  1008.  
  1009.     sprintf(tmp_20k_buf + strlen(tmp_20k_buf),
  1010.             "%s%c%s\\%s",
  1011.         MT_USER_FILE, MT_PATH_SEPARATOR,
  1012.         ps_global->pine_dir, MT_USER_FILE);
  1013. #else    /* !DOS */
  1014.     build_path(tmp_20k_buf, ps_global->home_dir, MT_STDPATH);
  1015. #endif    /* !DOS */
  1016.     pathcopy = cpystr(tmp_20k_buf);
  1017.     }
  1018.  
  1019.     path = pathcopy;            /* overloaded "path" */
  1020.  
  1021.     dprint(7, (debugfile, "mime_types: path: %s\n", path));
  1022.     while(path){
  1023.     if(s = strindex(path, MT_PATH_SEPARATOR))
  1024.       *s++ = '\0';
  1025.  
  1026.     if(rv = mt_browse_types_file(body, extension, path))
  1027.       break;
  1028.  
  1029.     path = s;
  1030.     }
  1031.   
  1032.     if(pathcopy)
  1033.       fs_give((void **)&pathcopy);
  1034.  
  1035.     return(rv);
  1036. }
  1037.  
  1038.  
  1039. /*
  1040.  * Try to match a file extension against extensions found in the file
  1041.  * ``filename'' if that file exists. return 1 if a match
  1042.  * was found and 0 in all other cases.
  1043.  */
  1044. int
  1045. mt_browse_types_file(body, extension, filename)
  1046.     BODY *body;
  1047.     char *extension;
  1048.     char *filename;
  1049. {
  1050.     int   rv = 0;
  1051.     FILE *file;
  1052.  
  1053.     if(file = fopen(filename, "r")){
  1054.     rv = mt_traverse_types_file(body, extension, file);
  1055.     fclose(file);
  1056.     }
  1057.     else
  1058.       dprint(1, (debugfile, "mt_browse: FAILED open(%s) : %s.\n",
  1059.          filename, error_description(errno)));
  1060.  
  1061.     return(rv);
  1062. }
  1063.  
  1064.  
  1065. /*
  1066.  * scan each line of the file. Treat each line as a mime type definition.
  1067.  * The first word is a type/subtype specification. All following words
  1068.  * are file extensions belonging to that type/subtype. Words are separated
  1069.  * bij whitespace characters.
  1070.  * If a file extension occurs more than once, then the first definition
  1071.  * determines the file type and subtype.
  1072.  */
  1073. int
  1074. mt_traverse_types_file(body, extension, file)
  1075.     BODY *body;
  1076.     char *extension;
  1077.     FILE *file;
  1078. {
  1079.     char buffer[LINE_BUF_SIZE];
  1080.  
  1081.     /* construct a loop reading the file line by line. Then check each
  1082.      * line for a matching definition.
  1083.      */
  1084.     while(fgets(buffer,LINE_BUF_SIZE,file) != NULL){
  1085.     char *typespec;
  1086.     char *try_extension;
  1087.  
  1088.     if(buffer[0] == '#')
  1089.       continue;        /* comment */
  1090.  
  1091.     /* remove last character from the buffer */
  1092.     buffer[strlen(buffer)-1] = '\0';
  1093.     /* divide the input buffer into words separated by whitespace.
  1094.      * The first words is the type and subtype. All following words
  1095.      * are file extensions.
  1096.      */
  1097.     dprint(5, (debugfile, "traverse: buffer=%s.\n", buffer));
  1098.     typespec = strtok(buffer," \t");    /* extract type,subtype  */
  1099.     dprint(5, (debugfile, "typespec=%s.\n", typespec));
  1100.     while((try_extension = strtok(NULL, " \t")) != NULL){
  1101.         /* compare the extensions, and assign the type if a match
  1102.          * is found.
  1103.          */
  1104.         dprint(5, (debugfile,"traverse: trying ext %s.\n", try_extension));
  1105.         if(strcmp(try_extension, extension) == 0){
  1106.         /* split the 'type/subtype' specification */
  1107.         char *type = strtok(typespec,"/");
  1108.         char *subtype = strtok(NULL,"/");
  1109.         dprint(5, (debugfile, "traverse: type=%s, subtype=%s.\n",
  1110.                type,subtype));
  1111.         /* The type is encoded as a small integer. we have to
  1112.          * translate the character string naming the type into
  1113.          * the corresponding number.
  1114.          */
  1115.         body->type = mt_translate_type(type);
  1116.         body->subtype = cpystr(subtype);
  1117.         return 1; /* a match has been found */
  1118.         }
  1119.     }
  1120.     }
  1121.  
  1122.     dprint(5, (debugfile, "traverse: search failed.\n"));
  1123.     return 0;
  1124. }
  1125.  
  1126.  
  1127. /*
  1128.  * Translate a character string representing a content type into a short 
  1129.  * integer number, according to the coding described in c-client/mail.h
  1130.  * List of content types taken from rfc1521, September 1993.
  1131.  */
  1132. int
  1133. mt_translate_type(type)
  1134.     char *type;
  1135. {
  1136.     int i;
  1137.  
  1138.     for (i=0;(i<=TYPEMAX) && body_types[i] && strucmp(type,body_types[i]);i++)
  1139.       ;
  1140.  
  1141.     if (i > TYPEMAX)
  1142.       i = TYPEOTHER;
  1143.     else if (!body_types[i])    /* if empty slot, assign it to this type */
  1144.       body_types[i] = cpystr (type);
  1145.  
  1146.     return(i);
  1147. }
  1148.